﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;

namespace OrbitOne.CRM.Plugins.Matchname
{
    public class MatchNamePlugin : IPlugin
    {
        #region Private variables

        private List<string> m_suffixes = new List<string>
                                              {
                                                  "nv",
                                                  "sa",
                                                  "vzw",
                                                  "bvba",
                                                  "cvba",
                                                  "sarl",
                                                  "sprl",
                                                  "scrl",
                                                  "nvsa",
                                                  "bv",
                                                  "llc",
                                                  "gmbh",
                                                  "ltd",
                                                  "ag",
                                                  "kt",
                                                  "og",
                                                  "vof",
                                                  "cvoa",
                                                  "commva",
                                                  "commv",
                                                  "ebvba",
                                                  "ez",
                                                  "belgium"
                                              };

        private Regex m_regex = new Regex("[^0-9A-Za-zà-ýÀ-Ý ()]");
        private CrmService m_service;

        #endregion

        #region Public methods
        public void Execute(IPluginExecutionContext context)
        {
            DynamicEntity entity = null;
            try
            {
                if (context.InputParameters.Properties.Contains("target") && context.InputParameters.Properties["target"] is DynamicEntity)
                {
                    entity = context.InputParameters.Properties["target"] as DynamicEntity;
                }
                if (entity == null) throw new NullReferenceException("target");

                if (context.Stage == MessageProcessingStage.BeforeMainOperationOutsideTransaction)
                {
                    var toNormalize = "";
                    string normalized;
                    if (context.MessageName == MessageName.Update)
                    {
                        var service = context.CreateCrmService(false);
                        var updateentity = service.Retrieve(entity.Name, ((Key)entity.Properties[entity.Name + "id"]).Value, new AllColumns());
                        switch (entity.Name)
                        {
                            case "contact":
                                toNormalize = entity.Properties.Contains("firstname")
                                    ? entity.Properties["firstname"] + " "
                                    : ((contact)updateentity).firstname + " ";
                                toNormalize += entity.Properties.Contains("lastname")
                                                   ? entity.Properties["lastname"].ToString()
                                                   : ((contact)updateentity).lastname;
                                normalized = normalize(toNormalize);
                                setFieldString(entity, "new_matchname", normalized);
                                break;
                            case "account":
                                toNormalize = entity.Properties.Contains("name")
                                    ? entity.Properties["name"].ToString()
                                    : ((account)updateentity).name;
                                normalized = normalize(toNormalize);
                                setFieldString(entity, "new_matchname", normalized);
                                break;
                            case "lead":
                                toNormalize = entity.Properties.Contains("firstname")
                                                  ? entity.Properties["firstname"] + " "
                                                  : ((lead)updateentity).firstname + " ";
                                toNormalize += entity.Properties.Contains("lastname")
                                                   ? entity.Properties["lastname"].ToString()
                                                   : ((lead)updateentity).lastname;
                                normalized = normalize(toNormalize);
                                setFieldString(entity, "new_matchcontact", normalized);

                                toNormalize = entity.Properties.Contains("name")
                                    ? entity.Properties["name"].ToString()
                                    : ((lead)updateentity).companyname;
                                normalized = normalize(toNormalize);
                                setFieldString(entity, "new_matchcompany", normalized);
                                break;
                            default:
                                throw new NullReferenceException("target");
                        }
                    }
                    else if (context.MessageName == MessageName.Create)
                    {
                        switch (entity.Name)
                        {
                            case "contact":
                                if (entity.Properties.Contains("firstname"))
                                    toNormalize = entity.Properties["firstname"] + " ";
                                if (entity.Properties.Contains("lastname"))
                                    toNormalize += entity.Properties["lastname"].ToString();
                                normalized = normalize(toNormalize.Trim());
                                setFieldString(entity, "new_matchname", normalized);
                                break;
                            case "account":
                                if (entity.Properties.Contains("name"))
                                    toNormalize = entity.Properties["name"].ToString();
                                normalized = normalize(toNormalize.Trim());
                                setFieldString(entity, "new_matchname", normalized);
                                break;
                            case "lead":
                                if (entity.Properties.Contains("firstname"))
                                    toNormalize = entity.Properties["firstname"] + " ";
                                if (entity.Properties.Contains("lastname"))
                                    toNormalize += entity.Properties["lastname"].ToString();
                                normalized = normalize(toNormalize.Trim());
                                setFieldString(entity, "new_matchcontact", normalized);

                                if (entity.Properties.Contains("companyname"))
                                    toNormalize = entity.Properties["companyname"].ToString();
                                normalized = normalize(toNormalize.Trim());
                                setFieldString(entity, "new_matchcompany", normalized);
                                break;
                            default:
                                throw new NullReferenceException("target");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new InvalidPluginExecutionException("An error occurred in the Matchname plug-in. " + ex, ex);
            }
        }
        #endregion

        #region Private methods
        private void setFieldString(DynamicEntity entity, string fieldName, string value)
        {
            if (!entity.Properties.Contains(fieldName))
            {
                entity.Properties.Add(new StringProperty(fieldName, value));
            }
            else
            {
                entity.Properties[fieldName] = value;
            }
        }
        private string normalize(string name)
        {
            name = (name ?? "").ToLower().Trim();
            name = m_regex.Replace(name, "");
            name = removeStuffBetweenParenthesis(name);
            name = removeSuffix(name);
            name = name.Replace(" ", "");
            name = removeSpecialCharacters(name);
            return name;
        }
        private string removeStuffBetweenParenthesis(string toClean)
        {
            while (toClean.Contains("(") && toClean.Contains(")"))
            {
                toClean = (toClean.IndexOf("(") != 0
                            ? toClean.Substring(0, toClean.IndexOf("(") - 1)
                            : "")
                       + toClean.Substring(toClean.IndexOf(")", toClean.IndexOf("(")) + 1);
            }
            return toClean;
        }
        private string removeSuffix(string toClean)
        {
            while (m_suffixes.Any(suffix => toClean.EndsWith(" " + suffix) || toClean.StartsWith(suffix + " ")))
            {
                foreach (var suffix in m_suffixes)
                {
                    if (toClean.EndsWith(" " + suffix))
                    {
                        toClean = toClean.Substring(0, toClean.Length - suffix.Length - 1);
                    }
                    if (toClean.StartsWith(suffix + " "))
                    {
                        toClean = toClean.Substring(suffix.Length + 1);
                    }
                }
            }

            return toClean;
        }
        private string removeSpecialCharacters(IEnumerable<char> toProcess)
        {
            var sb = new StringBuilder();
            foreach (var c in toProcess)
            {
                sb.Append(getSearchNameCharacter(c));
            }
            return new Regex("[^0-9A-Za-z]").Replace(sb.ToString(), "");
        }
        private static string getSearchNameCharacter(char character)
        {
            int code = character;
            string retval;
            switch (code)
            {
                case 138:
                    retval = "S";
                    break;
                case 140:
                    retval = "OE";
                    break;
                case 142:
                    retval = "Z";
                    break;
                case 154:
                    retval = "s";
                    break;
                case 156:
                    retval = "oe";
                    break;
                case 158:
                    retval = "z";
                    break;
                case 159:
                    retval = "Y";
                    break;
                case 197:
                case 196:
                case 195:
                case 194:
                case 193:
                case 192:
                    retval = "A";
                    break;
                case 198:
                    retval = "AE";
                    break;
                case 199:
                    retval = "C";
                    break;
                case 203:
                case 202:
                case 201:
                case 200:
                    retval = "E";
                    break;
                case 207:
                case 206:
                case 205:
                case 204:
                    retval = "I";
                    break;
                case 208:
                    retval = "D";
                    break;
                case 209:
                    retval = "N";
                    break;
                case 214:
                case 213:
                case 212:
                case 211:
                case 210:
                    retval = "O";
                    break;
                case 215:
                    retval = "x";
                    break;
                case 216:
                    retval = "O";
                    break;
                case 220:
                case 219:
                case 218:
                case 217:
                    retval = "U";
                    break;
                case 221:
                    retval = "Y";
                    break;
                case 223:
                    retval = "ss";
                    break;
                case 229:
                case 228:
                case 227:
                case 226:
                case 225:
                case 224:
                    retval = "a";
                    break;
                case 230:
                    retval = "oe";
                    break;
                case 231:
                    retval = "c";
                    break;
                case 235:
                case 234:
                case 233:
                case 232:
                    retval = "e";
                    break;
                case 239:
                case 238:
                case 237:
                case 236:
                    retval = "i";
                    break;
                case 240:
                    retval = "o";
                    break;
                case 241:
                    retval = "n";
                    break;
                case 246:
                case 245:
                case 244:
                case 243:
                case 242:
                    retval = "o";
                    break;
                case 248:
                    retval = "o";
                    break;
                case 252:
                case 251:
                case 250:
                case 249:
                    retval = "u";
                    break;
                case 253:
                    retval = "y";
                    break;
                case 255:
                    retval = "y";
                    break;
                default:
                    retval = character.ToString();
                    break;
            }

            return retval;
        }
        #endregion
    }
} 
